package com.stardata.liangshisq.tcslmon.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.stardata.liangshisq.tcslmon.entity.BizBs;
import com.stardata.liangshisq.tcslmon.entity.KcKsc;
import com.stardata.liangshisq.tcslmon.entity.OrderScheduleVo;
import com.stardata.liangshisq.tcslmon.mapper.ArchItemMapper;
import com.stardata.liangshisq.tcslmon.mapper.BizBsMapper;
import com.stardata.liangshisq.tcslmon.mapper.KcKscMapper;
import com.stardata.liangshisq.tcslmon.service.KcKscService;
import com.stardata.liangshisq.tcslmon.service.OrderQueueService;
import com.stardata.liangshisq.tcslmon.service.SysParameterService;
import com.stardata.liangshisq.tcslmon.util.CommonConstants;
import com.stardata.liangshisq.tcslmon.util.JSONUtil;
import lombok.AllArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author Samson Shu
 * @email shush@stardata.top
 * @date 2020/12/12 20:10
 */
@Service
@AllArgsConstructor
public class OrderQueueServiceImpl implements OrderQueueService {
  private final Logger logger = LoggerFactory.getLogger(this.getClass());

  private final KcKscMapper kcKscMapper;
  private final BizBsMapper bizBsMapper;
  private final ArchItemMapper archItemMapper;
  private final SysParameterService parameterService;
  private final KcKscService kcKscService;

  @Override
  public void refresh() {
    //1. 根据kc_ksc表生成bsIdsToPreparing（只要有菜品待配菜）列表、bsIdsPrepared（所有菜品已完成配菜）列表，
    // 以及每个bs_id对应的下单时间（首条菜品加单时间）
    QueryWrapper<KcKsc> kcKscQueryWrapper = new QueryWrapper<>();
    kcKscQueryWrapper.lt("kitchen_flg", CommonConstants.KitchenFlg.PRODUCED)
        .orderByAsc("create_time")
        //只考虑8小时内的后厨流水
        .apply("EXTRACT(epoch from (now()-create_time)) < 8*3600");

    List<KcKsc> kcKscList = kcKscMapper.selectList(kcKscQueryWrapper);
    //删除所有实际最终下单量为0的项目
    for (int i = kcKscList.size() - 1; i >= 0; i--) {
      KcKsc kcKsc = kcKscList.get(i);
      if (kcKsc.getLastQty().doubleValue()<0.001) {
        kcKscList.remove(i);
      }
    }
    //如果菜品的实际业务流水已经结算，则删除
    for (int i = kcKscList.size() - 1; i >= 0; i--) {
      KcKsc kcKsc = kcKscList.get(i);
      BizBs bizBs = bizBsMapper.selectById(kcKsc.getBsId());
      if (bizBs == null || bizBs.getState().equals(CommonConstants.OrderStatus.SETTLED)) {
        kcKscList.remove(i);
      }
    }
    //删除既不是KDS菜品、也不是临时菜品的记录
    KitchenQueryUtil.clearKcKscForNotKitchen(kcKscList, archItemMapper);

    //按后厨制作状态排序，确保后面处理时，
    // bs_id先进入 bsIdsPreparing 列表（如果还有菜在配）、后进入 bsIdsPrepared 列表（如果所有菜都是传菜中）
    kcKscList.sort((o1, o2) -> {
      if (o1.getKitchenFlg() < o2.getKitchenFlg()) {
        return -1;
      } else if (o1.getKitchenFlg() > o2.getKitchenFlg()) {
        return 1;
      }
      return 0;
    });

    //生成系统中实际还在配菜中 bsIdsPreparing 列表、以及完全配菜的 bsIdsPrepared 列表
    List<Long> bsIdsPreparing = new ArrayList<>(); //配菜中bsId列表
    List<Long> bsIdsPrepared = new ArrayList<>(); //配菜完成的bsId列表
    kcKscList.forEach(kcKsc -> {
      Long bsId = kcKsc.getBsId();
      if (kcKsc.getKitchenFlg().equals(CommonConstants.KitchenFlg.TO_PREPARE) &&
          !bsIdsPreparing.contains(bsId)) {
        bsIdsPreparing.add(bsId);
      }
      else if (kcKsc.getKitchenFlg().equals(CommonConstants.KitchenFlg.TO_PASSON) &&
          !bsIdsPreparing.contains(bsId) && !bsIdsPrepared.contains(bsId)) {
        bsIdsPrepared.add(bsId);
      }
    });
    //针对每个订单，找到它的首菜下单时间（即订单下单时间）
    Map<Long, LocalDateTime> bsOrderTimeMap = new HashMap<>(); //每个bsId对应的下单时间
    kcKscList.forEach(kcKsc -> {
      Long bsId = kcKsc.getBsId();
      if (bsOrderTimeMap.get(bsId) == null) {
        KcKsc kc = kcKscService.getFirstByBsId(bsId);
        if(kc!=null) {
          bsOrderTimeMap.put(bsId, kc.getCallUpTime());
        }else{
          bsOrderTimeMap.put(bsId, LocalDateTime.now());
        }
      }
    });

    //2. 将bsIdsPreparing列表、bsIdsPrepared列表，按照订单下单时间排序
    bsIdsPreparing.sort((o1, o2) -> {
      LocalDateTime dt1 = bsOrderTimeMap.get(o1);
      LocalDateTime dt2 = bsOrderTimeMap.get(o2);
      if (dt1.isBefore(dt2)) {
        return -1;
      } else if (dt1.isAfter(dt2)) {
        return 1;
      }
      return 0;
    });
    bsIdsPrepared.sort((o1, o2) -> {
      LocalDateTime dt1 = bsOrderTimeMap.get(o1);
      LocalDateTime dt2 = bsOrderTimeMap.get(o2);
      if (dt1.isBefore(dt2)) {
        return -1;
      } else if (dt1.isAfter(dt2)) {
        return 1;
      }
      return 0;
    });

    //3. 获取当前订单排队的3个队列：等待中、配菜中、配菜完成，以及插队订单列表
    List<Long> waitingOrderQueue = parameterService.getListByCode("waitingOrderQueue", Long.class);
    List<Long> preparingOrderQueue  = parameterService.getListByCode("preparingOrderQueue", Long.class);
    List<Long> preparedOrderQueue  = parameterService.getListByCode("preparedOrderQueue", Long.class);

    //4. 针对“配菜完成”队列的订单，如果其已不在 bsIdsPrepared 列表中，则出队列
    for (int i = preparedOrderQueue.size() - 1; i >= 0; i--) {
      Long id = preparedOrderQueue.get(i);
      if (!bsIdsPrepared.contains(id)) {
        //订单出队列
        preparedOrderQueue.remove(i);
        //清除可能存在的插队记录
        KitchenQueueUtil.getInstance().removeFromJumpOrders(id);
      }
    }

    //5. 将所有在 bsIdsPrepared、但不在"配菜完成"队列中的订单，入"配菜完成"队列
    // 这可能是刚刚全部配菜完成的、也可能是本来配菜未完成但退单后配菜完成的、也可能是"取消传菜"导致又出现在 bsIdsPrepared 的订单
    for (Long bsId : bsIdsPrepared) {
      if (!preparedOrderQueue.contains(bsId)) {
        preparedOrderQueue.add(bsId);
      }
    }

    //6. 针对“配菜中”队列的订单，如果其已不在 bsIdsPreparing 列表，则出“配菜中”队列
    for (int i = preparingOrderQueue.size() - 1; i >= 0; i--) {
      Long id = preparingOrderQueue.get(i);
      if (!bsIdsPreparing.contains(id) ) {
        preparingOrderQueue.remove(i);
      }
    }

    //7. 针对“配菜中”队列的订单，如果其进入了预定列表，则出“配菜中”队列
    List<OrderScheduleVo> orderSchedules  =
        parameterService.getListByCode("scheduledOrderQueue", OrderScheduleVo.class);
    List<Long> scheduleBsIds = new ArrayList<>();
    for (OrderScheduleVo orderSchedule : orderSchedules) {
      if (!scheduleBsIds.contains(orderSchedule.getBsId())) {
        scheduleBsIds.add(orderSchedule.getBsId());
      }
    }
    for (Long bsId : scheduleBsIds) {
      preparingOrderQueue.remove(bsId);
    }

    //8. 针对“等待中”队列的订单，如果其已不在bsIdsPreparing列表，则出“等待中”队列
    for (int i = waitingOrderQueue.size() - 1; i >= 0; i--) {
      if (!bsIdsPreparing.contains(waitingOrderQueue.get(i))) {
        waitingOrderQueue.remove(i);
      }
    }

    //9. 将足够多的订单，排队进入"配菜中"订单队列

    //9.1 先从“等待中”队列的订单中，选取足够多的订单，进入"配菜中"订单队列
    while ((waitingOrderQueue.size()>0) &&
        (preparingOrderQueue.size() < KitchenQueueUtil.getInstance().getMaxPreparingSize())) {
      Long id = waitingOrderQueue.get(0);
      preparingOrderQueue.add(id);
      waitingOrderQueue.remove(0);
    }

    //9.2 再从 bsIdsPreparing 列表，选取足够多的订单，入“配菜中”订单队列
    bsIdsPreparing.removeAll(preparingOrderQueue);  //开始之前，先删除"配菜中"队列已有的订单
    int idx = 0;
    while (idx< bsIdsPreparing.size()) {
      if (preparingOrderQueue.size() >= KitchenQueueUtil.getInstance().getMaxPreparingSize()) break;

      Long id = bsIdsPreparing.get(idx);
      if (!scheduleBsIds.contains(id)) {
        preparingOrderQueue.add(id);
        bsIdsPreparing.remove(idx);
      } else {
        idx++;
      }
    }

    //10. 将 bsIdsToPreparing 列表中剩余订单入“等待中”订单队列
    for (Long bsId : bsIdsPreparing) {
      if (!waitingOrderQueue.contains(bsId)) {
        waitingOrderQueue.add(bsId);
      }
    }

    //11. 删除 waitingOrderQueue 中已经设置为预定的订单，以及不在 bsIdsPreparing 中的订单（可能是退单所致）
    for (Long bsId : scheduleBsIds) {
      waitingOrderQueue.remove(bsId);
    }

    //12. 保存3个订单队列：等待中、配菜中、配菜完成
    parameterService.saveByCode("waitingOrderQueue", JSONUtil.toJSONString(waitingOrderQueue));
    parameterService.saveByCode("preparingOrderQueue", JSONUtil.toJSONString(preparingOrderQueue));
    parameterService.saveByCode("preparedOrderQueue", JSONUtil.toJSONString(preparedOrderQueue));
  }

}
